package main

import (
	"fmt"
	"sync/atomic"
	"time"
)

const (
	DEFAULT_ALLOWED_VISIT_PER_SECOND = 10
	DEFAULT_BUCKET_NUMS              = 10
)

type Counter struct {
	// 总限额
	quota int64
	// 当前请求的数量
	reqNums int64
	// 桶的数量
	bucketNums int
	// 保存每个桶对应的请求数
	buckets []int64
	// 记录当前桶的索引
	index int
	// bucket起始index
	begin int
	// bucket结尾index
	end int
}

// 每100ms执行一次
func (c *Counter) setIndex() {
	ticker := time.NewTicker(time.Millisecond * 100)
	for {
		select {
		case <-ticker.C:
			c.index = (c.index + 1) % (c.bucketNums + 1)
			// 重置end的下一个bucket
			c.buckets[(c.end+1)%(c.bucketNums+1)] = 0
			// 移动begin和end
			c.begin = (c.begin + 1) % (c.bucketNums + 1)
			c.end = (c.end + 1) % (c.bucketNums + 1)
			fmt.Println("index:", c.index)
			fmt.Println("begin", c.begin)
			fmt.Println("end", c.end)
		}
	}
}

func (c *Counter) isOverLimit() bool {
	currentReqNums := c.getRollingSum()
	if currentReqNums >= c.quota {
		return true
	}
	// 可以访问时，增加桶的计数
	atomic.AddInt64(&c.buckets[c.index], 1)
	return false
}

func (c *Counter) getRollingSum() int64 {
	var rollingSum int64
	if c.end < c.begin {
		for i := 0; i <= c.end; i++ {
			rollingSum += c.buckets[i]
		}
		for j := c.begin; j < c.bucketNums+1; j++ {
			rollingSum += c.buckets[j]
		}
	} else {
		for i := c.begin; i <= c.end; i++ {
			rollingSum += c.buckets[i]
		}
	}
	return rollingSum
}

func (c *Counter) init() {
	if c.bucketNums == 0 {
		c.bucketNums = DEFAULT_BUCKET_NUMS
	}
	if c.quota == 0 {
		c.quota = DEFAULT_ALLOWED_VISIT_PER_SECOND
	}
	// 初始化bucket, 一共c.bucketNums + 1个元素
	for i := 0; i < c.bucketNums+1; i++ {
		c.buckets = append(c.buckets, 0)
	}
	// 初始化begin和end
	c.begin = 0
	c.end = c.bucketNums - 1
	// 开启协程，更新index
	go c.setIndex()
}

func main() {
	c := &Counter{}
	c.init()
	for i := 0; i < 100; i++ {
		if i == 30 {
			time.Sleep(100 * time.Millisecond)
		}
		if c.isOverLimit() {
			fmt.Println("over limit, index:", i)
		} else {
			fmt.Println("can access, index", i)
		}
	}
	select {}
}
